package com.wiscamp.ninechapters.tags.infrastructure.repositories;


import com.wiscamp.ninechapters.common.GlobalState;
import com.wiscamp.ninechapters.tags.domain.models.Tag;
import com.wiscamp.ninechapters.tags.domain.models.TagGroup;
import com.wiscamp.ninechapters.tags.domain.models.TagName;
import com.wiscamp.ninechapters.tags.domain.repositories.TagRepository;
import com.wiscamp.ninechapters.tags.infrastructure.converters.TagConverter;
import com.wiscamp.ninechapters.tags.infrastructure.mappers.TagMapper;
import com.wiscamp.ninechapters.tags.infrastructure.mappers.TagViewMapper;
import com.wiscamp.ninechapters.tags.infrastructure.po.TagPO;
import cube.common.core.CollectionUtils;
import cube.ddd.aspect.HandleDataException;
import cube.ddd.domain.BaseRepository;
import cube.ddd.utils.PersistenceUtils;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;

@Repository
public class TagRepositoryImpl extends BaseRepository implements TagRepository {

    private final TagMapper tagMapper;

    private final TagViewMapper tagViewMapper;

    public TagRepositoryImpl(TagMapper tagMapper, TagViewMapper tagViewMapper) {
        this.tagMapper = tagMapper;
        this.tagViewMapper = tagViewMapper;
    }


    // region tag

    private TagPO getTagPO(long id) {
        return PersistenceUtils.findById(id, tagMapper::findById);
    }

    @HandleDataException
    public Tag getTag(@Min(0) long tagId) {
        return PersistenceUtils.findEntityById(tagId, tagViewMapper::findById,
                TagConverter.Instance::toTagByView);
    }

    @HandleDataException
    public Tag getTagByCode(@NotBlank String tagCode, long appId) {
        return PersistenceUtils.getEntityByTextAndId(tagCode, appId, tagViewMapper::findByTagCode,
                TagConverter.Instance::toTagByView);
    }

    @HandleDataException
    public Collection<Tag> getTagsByGroupName(@NotBlank String groupName, long appId) {
        return PersistenceUtils.getEntitiesByTextAndId(groupName, appId, tagViewMapper::findListByGroupName,
                TagConverter.Instance::toTagByView);
    }

    @HandleDataException
    public Tag getTagByName(@NotBlank String groupName, @NotBlank String tagName, @Min(0) long appId) {
        var tagView = tagViewMapper.findByName(groupName, tagName, appId);
        if (Objects.nonNull(tagView))
            return TagConverter.Instance.toTagByView(tagView);
        return null;
    }

    @HandleDataException
    public Collection<Tag> getTagsByIds(@NotEmpty Collection<Long> tagIds) {
        var tagViews = tagViewMapper.findListByTagIds(tagIds);
        return CollectionUtils.toArrayList(tagViews, TagConverter.Instance::toTagByView);
    }

    @HandleDataException
    public Collection<TagName> getTagNamesByIds(@NotEmpty Collection<Long> tagIds) {
        var tags = getTagsByIds(tagIds);
        var tagNames = new ArrayList<TagName>();
        for (var view : tags) {
            var tagName = new TagName();
            tagName.setTagId(view.getTagId());
            tagName.setTagName(view.getTagName());
            tagName.setGroupName(view.getGroupName());
            tagNames.add(tagName);
        }
        return tagNames;
    }

    @HandleDataException
    public Tag saveTag(@NotNull Tag tag) {
        var newId = tag.getTagId();
        if (tag.getTagId() <= 0) {
            var newTag = PersistenceUtils.createEntity(tag, tagMapper::saveAndFlush,
                    TagConverter.Instance::toTagPO, TagConverter.Instance::toTag);
            newId = newTag.getTagId();
        } else {
            var existedPO = getTagPO(tag.getTagId());
            if (existedPO == null) existedPO = new TagPO();
            PersistenceUtils.updateEntity(tag, existedPO, tagMapper::saveAndFlush,
                    TagConverter.Instance::toTagPO, TagConverter.Instance::toTag);
        }
        return getTag(newId);
    }

    // endregion


    public Collection<TagGroup> getAvailableTagGroups() {
        var groupNames = tagViewMapper.findAvailableGroups();
        var groups = new ArrayList<TagGroup>();
        long count = 1;
        for (var name : groupNames) {
            var group = new TagGroup();
            group.setApplicationId(GlobalState.APPLICATION_ID);
            group.setGroupId(count);
            group.setGroupName(name);
            groups.add(group);
            count++;
        }
        return groups;
    }
}
